package com.loser.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 上下文工具
 *
 * @author alan
 * @date 2022/5/21 10:10
 */
@SuppressWarnings("all")
public class ContextUtil {

    private static final Logger log = LoggerFactory.getLogger(ContextUtil.class);
    private static ApplicationContext applicationContext = null;
    private static final int STAY_TIME = 100;
    private static final int STAY_COUNT = 10;
    private static final Map<Class<?>, Object> CACHE = new ConcurrentHashMap<>();

    public static void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ContextUtil.applicationContext = applicationContext;
    }

    /**
     * applicationContext
     */
    public static ApplicationContext getApplicationContext() {

        if (Objects.nonNull(applicationContext)) {
            return applicationContext;
        }

        // 循环十次等等容器初始华
        for (int i = 0; i < STAY_COUNT; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(STAY_TIME);
                if (Objects.nonNull(applicationContext)) {
                    return applicationContext;
                }
            } catch (Exception ex) {
                log.error("getApplicationContext sleep throw a e ", ex);
            }
        }
        if (Objects.isNull(applicationContext)) {
            throw new RuntimeException("getApplicationContext throw a e : context is null");
        }
        return applicationContext;

    }

    /**
     * 通过name获取 Bean. 需要再springboot启动的时候设置applicationContext
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean
     */
    public static <T> T getBean(Class<T> clazz) {

        Object result = CACHE.get(clazz);
        if (Objects.nonNull(result)) {
            return (T) result;
        }
        T bean = getApplicationContext().getBean(clazz);
        CACHE.put(clazz, bean);
        return bean;

    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    public static <T> List<T> getBeans(Class<T> clazz) {
        return getBeans(getApplicationContext(), clazz);
    }

    public static <T> List<T> getBeans(ApplicationContext applicationContext, Class<T> clazz) {

        return Arrays.stream(applicationContext.getBeanNamesForType(clazz))
                .map(item -> (T) applicationContext.getBean(item)).collect(Collectors.toList());

    }

}
